Repository navigation
Add spilo_run_as_non_root option to keep pod spec in sync with webhook-injected security context - #3209
Open
sled30 wants to merge 1 commit into
Open
Add spilo_run_as_non_root option to keep pod spec in sync with webhook-injected security context#3209sled30 wants to merge 1 commit into
sled30 wants to merge 1 commit into
Conversation
…k-injected security context Mutating admission webhooks (e.g. Kyverno Pod Security policies) may inject `runAsNonRoot` into the pod and the Spilo container security context of the operator-generated StatefulSet. The operator cannot emit this field, so the desired pod template never matches the live state: on every resync the operator detects "pod changes" and triggers a full rolling update with a switchover, creating a restart loop for the whole PostgreSQL cluster. Add a `spilo_run_as_non_root` operator option and a `spiloRunAsNonRoot` cluster manifest field. When set, the operator includes `runAsNonRoot` in both the pod security context and the Spilo container security context, so the generated spec matches the webhook-enforced state and the rolling update loop stops. The manifest field overrides the global option. Unset by default: `runAsNonRoot` is omitted from the generated pod spec, preserving the current behavior. CRDs, deepcopy code and documentation are regenerated accordingly.
sled30
requested review from
FxKu,
Jan-M,
hughcapet,
idanovinda,
jopadi and
mikkeloscar
as code owners
October 30, 2026 09:23
Contributor
|
Good lock, I wait from 2022 to fix it on my PR :-( |
3 tasks done
yyvess
pushed a commit
to yyvess/postgres-operator
that referenced
this pull request
Nov 7, 2026
Align the option name with the one proposed in zalando#3209. runAsNonRoot is now also filled into the Spilo, sidecar, init and logical backup containers, unless they define it themselves or run privileged. Admission webhooks enforcing runAsNonRoot (e.g. Kyverno) inject it on container level too; with only the pod field set, the generated StatefulSet kept differing from the mutated one and the operator rolled the cluster on every sync (zalando#3210).
Contributor
|
I try to address all security option in one new clean PR => #3224 |
Author
Thanks a lot! For now, I’ll keep the PR open-just in case something doesn’t look right. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Mutating admission webhooks (e.g. Kyverno Pod Security policies) may inject
runAsNonRootinto the pod and the Spilo container security context of theoperator-generated StatefulSet. The operator cannot emit this field: the
configuration offers
spilo_runasuser/spilo_runasgroup/spilo_fsgroup,spilo_privilegedandspilo_allow_privilege_escalation-but no
runAsNonRootknob. Therefore the desired pod template never matchesthe live state, and on every resync the operator detects "pod changes" and
performs a full rolling update with a Patroni switchover:
Newly created pods are mutated by the webhook again, so the cluster never
converges: in our environment the whole PostgreSQL cluster restarted with a
leader switchover on every resync period (30 minutes, ~48 restarts/day per
cluster). This is the same class of problem as PR #3095 (webhook-injected
sysctls) and issue #1638 (GKE Autopilot mutating resources/annotations).Fixes #3210
Related: #3095, #1638
Change
Add a
spilo_run_as_non_rootoperator option (OperatorConfiguration,kubernetessection) and aspiloRunAsNonRootcluster manifest field thatoverrides the global option. When set, the operator includes
runAsNonRootin both the pod security context (
generatePodTemplate) and the Spilocontainer security context (
generateContainer; the logical backup containerfollows the existing
spilo_privileged/spilo_allow_privilege_escalationpattern). Unset by default:
runAsNonRootis omitted from the generated podspec, preserving the current behavior.
Files
pkg/util/config/config.go—SpiloRunAsNonRoot *bool(spilo_run_as_non_root)pkg/apis/acid.zalan.do/v1/operator_configuration_type.go— matching CRD config typepkg/apis/acid.zalan.do/v1/postgresql_type.go—spiloRunAsNonRootinPostgresSpecpkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go— deepcopy for the new fieldmanifests/postgresql.crd.yaml,manifests/operatorconfiguration.crd.yaml,pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml,pkg/apis/acid.zalan.do/v1/operatorconfiguration.crd.yaml,charts/postgres-operator/crds/*— regenerated CRDspkg/cluster/k8sres.go— emitRunAsNonRootat pod and container levelpkg/cluster/k8sres_test.go—TestSpiloRunAsNonRootmanifests/postgresql-operator-default-configuration.yaml,docs/reference/operator_parameters.md,docs/reference/cluster_manifest.md— examples and reference docsTest
go build ./...,go vet ./pkg/cluster/ ./pkg/util/config/and the full./pkg/cluster/,./pkg/util/...suites pass. New focused test:Scope / operations notes
OperatorConfiguration CRD), like the other scalar
spilo_*options;rolling update after enabling the option (the generated spec changes), then
the loop stops;
runAsNonRoot, which is the field our enforcing policyinjects; other webhook-injected fields (e.g.
sysctls, see feat: add pod_sysctls operator config option #3095) wouldneed analogous options.